﻿using System;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml;

namespace RSSReader
{
  public class GoogleReaderAPI
  {
    public class UnreadListEventArgs : System.EventArgs
    {
      public class UnreadFeed
      {
        public string FeedUrl { get; set; }
        public int NumUnread { get; set; }
        public bool IsAllFeed { get; set; }
        public UnreadFeed(string url, int unread, bool isAllFeed = false)
        {
          FeedUrl = url;
          NumUnread = unread;
          IsAllFeed = isAllFeed;
        }
      }

      public int MinUnread { get; set; }
      public int UnreadRange { get; set; }
      public List<UnreadFeed> Feeds { get; set; }
      public UnreadListEventArgs()
      {
        Feeds = new List<UnreadFeed>();
      }
    }
    public delegate void UnreadListReadyHandler(Object sender, UnreadListEventArgs e);
    public event UnreadListReadyHandler UnreadListReady;
    
    public class FeedListEventArgs : System.EventArgs
    {
      public class Feed
      {
        public string FeedUrl { get; set; }
        public string FeedTitle { get; set; }
        public int NewCount { get; set; }
        public Feed(string url, string title, int count=0)
        {
          FeedUrl = url;
          FeedTitle = title;
          NewCount = count;
        }
      }

      public int MinUnread { get; set; }
      public int UnreadRange { get; set; }
      public List<Feed> Feeds { get; set; }
      public FeedListEventArgs()
      {
        Feeds = new List<Feed>();
      }
    }
    public delegate void FeedListReadyHandler(Object sender, FeedListEventArgs e);
    public event FeedListReadyHandler FeedListReady;

    public class FeedItemsEventArgs : System.EventArgs
    {
      public class FeedItem
      {
        public string Title;
        public string Body;
        public string PageUrl;
        public DateTime Time;

        public FeedItem(string title, string body, string pageUrl, DateTime time)
        {
          Title = title;
          body = Body;
          PageUrl = pageUrl;
          Time = time;
        }
      }

      public string Title;
      public List<FeedItem> Items;

      public FeedItemsEventArgs()
      {
        Items = new List<FeedItem>();
      }
    }
    public delegate void FeedItemsReadyHandler(Object sender, FeedItemsEventArgs e);
    public event FeedItemsReadyHandler FeedItemsReady;

    public delegate void ReaderReadyHandler(Object sender, EventArgs e);
    public event ReaderReadyHandler ReaderReady;

    public delegate void RefreshDoneHandler(Object sender, EventArgs e);
    public event RefreshDoneHandler RefreshDone;

    // Query URLs into the Google Reader API
    private static string URL = "http://www.google.com";
    private static string CLIENTLOGIN = "/accounts/ClientLogin";
    private static string TOKEN = "/token";
    private static string READERAPI = "/reader/api/0";
    private static string SUBSCRIPTIONSGET = "/subscription/list";
    private static string UNREADCOUNT = "/unread-count";
    private static string USERINFO = "/user-info";
    private static string FEED = "/reader/atom/";

    private string mAuth = null;
    private string mToken = null;
    private int mFeedsToDownload, mFeedsDownloaded;
    public string UserName;
    public string Password;
    public string LastMsg { get; set; }

    public bool IsReady { get; private set; }

    public GoogleReaderAPI()
    {
      IsReady = false;
    }
    public GoogleReaderAPI(string username, string password)
    {
      UserName = username;
      Password = password;
      IsReady = false;
    }

    public void connect()
    {
      getAuth();
    }

    private void getAuth()
    {
      WebClient client = new WebClient();
      client.AllowReadStreamBuffering = true;
      client.DownloadStringCompleted += gotAuth;
      ParameterizedUri p = new ParameterizedUri(URL + CLIENTLOGIN, false);
      p["accountType"] = "GOOGLE";
      p["service"] = "reader";
      p["Email"] = UserName;
      p["Passwd"] = Password;
      client.DownloadStringAsync(p.toURI());
    }

    private void gotAuth(Object sender, DownloadStringCompletedEventArgs e)
    {
      mAuth = e.Result.Split('\n')[2].Substring("Auth=".Length);
      getToken();
    }

    private void getToken()
    {
      ParameterizedUri p = new ParameterizedUri(URL + READERAPI + TOKEN, false);
      WebClient client = getRequestClient(gotToken);
      client.DownloadStringAsync(p.toURI());
    }

    private void gotToken(Object sender, DownloadStringCompletedEventArgs e)
    {
      mToken = e.Result;
      IsReady = true;
      ReaderReady(this, new EventArgs());
    }

    private WebClient getRequestClient(DownloadStringCompletedEventHandler slot)
    {
      WebClient client = new WebClient();
      client.Headers["Authorization"] = "GoogleLogin auth=" + mAuth + "";
      client.DownloadStringCompleted += slot;
      return client;
    }

    private WebClient getPostClient(DownloadStringCompletedEventHandler slot)
    {
      WebClient client = new WebClient();
      client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
      client.Headers["Authorization"] = "GoogleLogin auth=" + mAuth + "";
      client.DownloadStringCompleted += slot;
      return client;
    }

    public void getFeedList(bool forceQuery=false)
    {
      if (!forceQuery && App.SaveData.Feeds.Count > 0)
      {
        int min=0, max=0;
        bool first = true;
        FeedListEventArgs flea = new FeedListEventArgs();
        foreach (KeyValuePair<string, SaveData.FeedInformation> f in App.SaveData.Feeds)
        {
          if (!f.Value.Title.Equals(App.ALLITEMSNAME))
          {
            if (first || min > f.Value.NewCount) min = f.Value.NewCount;
            if (first || max < f.Value.NewCount) max = f.Value.NewCount;
            first = false;
          }
          flea.Feeds.Add(new FeedListEventArgs.Feed(f.Value.Url, f.Value.Title, f.Value.NewCount));
        }
        flea.UnreadRange = max - min;
        flea.MinUnread = min;
        FeedListReady(this, flea);
      }
      else
      {
        queryFeedList();
      }
    }

    public void queryFeedList()
    {
      WebClient client = getRequestClient(gotFeedList);
      ParameterizedUri p = new ParameterizedUri(URL + READERAPI + SUBSCRIPTIONSGET, false);
      p["output"] = "xml";
      p["client"] = "WP7RSSTest";
      client.DownloadStringAsync(p.toURI());
    }

    private void gotFeedList(Object sender, DownloadStringCompletedEventArgs e)
    {
      FeedListEventArgs flea = new FeedListEventArgs();
      using (XmlReader reader = XmlReader.Create(new StringReader(e.Result)))
      {
        reader.ReadToFollowing("list");
        while (reader.ReadToFollowing("object"))
        {
          reader.ReadToFollowing("string");
          string url = reader.ReadInnerXml(),
                 title = reader.ReadInnerXml();
          App.SaveData.addFeed(new SaveData.FeedInformation(title, url));
          flea.Feeds.Add(new FeedListEventArgs.Feed(url, title));
        }
      }
      flea.UnreadRange = 0;
      FeedListReady(this, flea);
    }

    public void queryUnreadList()
    {
      WebClient client = getRequestClient(gotUnreadList);
      ParameterizedUri p = new ParameterizedUri(URL + READERAPI + UNREADCOUNT, false);
      p["allcomments"] = "true";
      p["output"] = "xml";
      p["ck"] = Convert.ToString(currentUnixTime());
      p["client"] = "WP7RSSTest";
      client.DownloadStringAsync(p.toURI());
    }

    private void gotUnreadList(Object sender, DownloadStringCompletedEventArgs e)
    {
      UnreadListEventArgs ulea = new UnreadListEventArgs();
      LastMsg = e.Result;
      mFeedsToDownload = 0;
      mFeedsDownloaded = 0;
      using (XmlReader reader = XmlReader.Create(new StringReader(e.Result)))
      {
        int min = 0, max = 0;
        bool first = true, isAllFeed=false;
        reader.ReadToFollowing("list");
        while (reader.ReadToFollowing("object"))
        {
          reader.ReadToFollowing("string");
          string url = reader.ReadInnerXml();
          int unread = Convert.ToInt32(reader.ReadInnerXml());
          SaveData.FeedInformation fi = App.SaveData.getFeedFromUrl(url);
          if (fi != null)
          {
            fi.NewCount = unread;
            App.SaveData.updateFeed(fi);
          }
          isAllFeed = url.Contains("/state/com.google/reading-list");
          if (!isAllFeed)
          {
            if (first || min > fi.NewCount) min = fi.NewCount;
            if (first || max < fi.NewCount) max = fi.NewCount;
            first = false;
          }

          queryFeedItems(url, App.SaveData.OnlyGetUnreadFeeds ? unread : App.SaveData.FeedsToDownload);
          ulea.Feeds.Add(new UnreadListEventArgs.UnreadFeed(url, unread, isAllFeed));
          ++mFeedsToDownload;
        }
        ulea.MinUnread = min;
        ulea.UnreadRange = max - min;
      }
      UnreadListReady(this, ulea);
    }

    public void queryFeedItems(string url, int numFeeds=25)
    {
      WebClient client = getRequestClient(gotFeedItems);
      ParameterizedUri p = new ParameterizedUri(URL + FEED + url, false);
      p["client"] = "WP7RSSTest";
      p["n"] = Convert.ToString(numFeeds);
      Debug.WriteLine(p.toURI().AbsoluteUri);
      client.DownloadStringAsync(p.toURI());
    }

    public void gotFeedItems(Object sender, DownloadStringCompletedEventArgs e)
    {
      XmlReaderSettings settings = new XmlReaderSettings();
      settings.IgnoreWhitespace = true;
      using (XmlReader reader = XmlReader.Create(new StringReader(e.Result), settings))
      {
        List<SaveData.FeedItem> items = new List<SaveData.FeedItem>();
        reader.ReadToFollowing("title");
        string feedTitle = reader.ReadInnerXml();
        int i = 0;
        while (reader.ReadToFollowing("entry"))
        {
          ++i;
          using (XmlReader entryReader = reader.ReadSubtree())
          {
            string title = "", body = "", url = "", stringTime = "";
            DateTime dtime = DateTime.Now;
            bool pass = false, done = false;
            while (!done && (pass || entryReader.Read()))
            {
              pass = false;
              if (entryReader.IsStartElement())
              {
                if (entryReader.Name.Equals("title"))
                {
                  title = entryReader.ReadInnerXml();
                  pass = true;
                }
                else if (entryReader.Name.Equals("link") && entryReader["rel"].Equals("alternate"))
                {
                  url = entryReader["href"];
                }
                else if (entryReader.Name.Equals("published"))
                {
                  stringTime = entryReader.ReadInnerXml();
                  string[] parts = stringTime.Split('T');
                  string[] date = parts[0].Split('-');
                  string[] time = parts[1].Split(':');
                  dtime = new DateTime(Convert.ToInt32(date[0]), Convert.ToInt32(date[1]), Convert.ToInt32(date[2]),
                                       Convert.ToInt32(time[0]), Convert.ToInt32(time[1]), Convert.ToInt32(time[2].Substring(0, 2)));
                  pass = true;
                }
                else if (entryReader.Name.Equals("summary"))
                {
                  body = entryReader.ReadInnerXml();
                  pass = true;
                  done = true;
                }
                else if (entryReader.Name.Equals("content"))
                {
                  body = entryReader.ReadInnerXml();
                  pass = true;
                  done = true;
                }
              }
            }
            entryReader.Close();
            items.Add(new SaveData.FeedItem(title, body, url, dtime));
          }
        }
        ++mFeedsDownloaded;
        if (mFeedsDownloaded >= mFeedsToDownload)
        {
          RefreshDone(this, new EventArgs());
        }
        if (feedTitle.Contains("reading list in Google Reader"))
          App.SaveData.updateFeedItems(App.ALLITEMSNAME, items);
        else
          App.SaveData.updateFeedItems(feedTitle, items);
        reader.Close();
      }
    }

    private long currentUnixTime()
    {
      return (long)((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
    }
  }
}
